package com.wym.exampleproject.distributedzookeeperlock.service.impl;

import com.wym.exampleproject.distributedzookeeperlock.service.IDistributedZkLockService;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.api.CuratorListener;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.log4j.helpers.ThreadLocalMap;
import org.apache.zookeeper.CreateMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Service
public class DistributedZkLockServiceImpl implements IDistributedZkLockService {
    private ThreadLocal<String> myPath = new ThreadLocal<>();
    @Autowired
    private CuratorFramework client;
    private static String rootPath = "/zkLockPath";
    private static String lockPath = rootPath + "/lock-";

    @Bean(initMethod = "start")
    public void afterPropertiesSet() {
        client.start();
        //判断父节点是否存在,不存在新建
        try {
            if (client.checkExists().forPath(rootPath) == null) {
                client.create().forPath(rootPath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean lock() {
        boolean isHaveLock = false;
        try {
            //创建新的临时有序节点
            myPath.set(client.create().
                    withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
                    forPath(lockPath));
            log.info("当前节点名字:" + myPath.get());
            //获取所有临时有序节点并排序
            List<String> sortNodePaths = getSortNodePaths();
            for (String sortNodePath : sortNodePaths) {
                log.info("集合：" + sortNodePath);
            }
            //判断是否当前节点最小
            if (getHaveLock(myPath.get(), sortNodePaths)) {
                isHaveLock = true;
            } else {
                //计算当前节点的上一个节点
                String lastNodePath = getLastNodePath(myPath.get());
                final CountDownLatch countDownLatch = new CountDownLatch(1);
                final PathChildrenCache cache = new PathChildrenCache(client, rootPath, false);
                cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
                cache.getListenable().addListener((client, event) -> {
                    if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
                        log.info("判断节点是否存在:"+lastNodePath);
                        if (client.checkExists().forPath(lastNodePath) == null) {
                            //释放计数器，让当前的请求获取锁
                            countDownLatch.countDown();
                            System.out.println("匹配成功");
                        }
                    }
                });
                countDownLatch.await();
                log.info(myPath.get() + ":获取锁");
                isHaveLock = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isHaveLock;
    }

    private String getLastNodePath(String myPath) {
        Integer len = 10;
        Long number = Long.parseLong(myPath.split("-")[1]) - 1;
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < len - number.toString().length(); i++) {
            stringBuffer.append("0");
        }
        return lockPath + stringBuffer.toString() + number;
    }

    private boolean getHaveLock( String myPath, List<String> sortNodePaths) {
        boolean isHaveLock = false;
        String[] myPaths = myPath.split("-");
        String[] sortNode = sortNodePaths.get(0).split("-");
        if (myPaths[1].equals(sortNode[1])) {
            isHaveLock = true;
        }
        return isHaveLock;
    }

    private List<String> getSortNodePaths() throws Exception {
        List<String> allPaths = client.getChildren().forPath(rootPath);
        log.info("获取子节点数量:" + allPaths.size());
        Collections.sort(allPaths, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return getLockNodeNumber(o1, lockPath).compareTo(getLockNodeNumber(o2, lockPath));
            }
        });
        return allPaths;
    }

    private String  getLockNodeNumber(String str, String lockName) {
        int index = str.lastIndexOf(lockName);
        if (index >= 0) {
            index += lockName.length();
            return index <= str.length() ? str.substring(index) : "";
        }
        return str;
    }

    @Override
    public void unLock() {
        try {
            if (client.checkExists().forPath(myPath.get()) != null) {
                client.delete().forPath(myPath.get());
            }
            log.info("解锁路径:" + myPath.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }



}
